#ifndef _LUA_HANDLER_H_
#define _LUA_HANDLER_H_

#include <vector>
typedef struct lua_State lua_State;

class LuaHandler
{
public:
	LuaHandler();
	LuaHandler(int handler);
	~LuaHandler();

	bool operator==(int handler);
	LuaHandler& operator=(int handler);
public:
	template <typename T>
	void push(T arg);

	template <typename TP1, typename TP2, typename TP3, typename TP4, typename TP5, typename TP6>
		void invoke(TP1 p1, TP2 p2, TP3 p3, TP4 p4, TP5 p5, TP6 p6)
		{
			prepare();

			push<TP1>(p1);
			push<TP2>(p2);
			push<TP3>(p3);
			push<TP4>(p4);
			push<TP5>(p5);
			push<TP6>(p6);

			exec(6);
		}

		template <typename TP1, typename TP2, typename TP3, typename TP4, typename TP5>
		void invoke(TP1 p1, TP2 p2, TP3 p3, TP4 p4, TP5 p5)
		{
			prepare();
			push<TP1>(p1);
			push<TP2>(p2);
			push<TP3>(p3);
			push<TP4>(p4);
			push<TP5>(p5);
			exec(5);
		}

		template <typename TP1, typename TP2, typename TP3, typename TP4>
		void invoke(TP1 p1, TP2 p2, TP3 p3, TP4 p4)
		{
			prepare();
			push<TP1>(p1);
			push<TP2>(p2);
			push<TP3>(p3);
			push<TP4>(p4);
			exec(4);
		}

		template <typename TP1, typename TP2, typename TP3>
		void invoke(TP1 p1, TP2 p2, TP3 p3)
		{
			prepare();
			push<TP1>(p1);
			push<TP2>(p2);
			push<TP3>(p3);
			exec(3);
		}

		template <typename TP1, typename TP2>
		void invoke(TP1 p1, TP2 p2)
		{
			prepare();
			push<TP1>(p1);
			push<TP2>(p2);
			exec(2);
		}

		template <typename TP1>
		void invoke(TP1 p1)
		{
			prepare();
			push<TP1>(p1);
			exec(1);
		}

		void invoke()
		{
			prepare();
			exec(0);
		}
private:
	virtual void pushInt(int value);
	virtual void pushBool(bool value);
	virtual void pushFloat(float value);
	virtual void pushLongLong(long long a);
	virtual void pushString(const std::string& value);
	virtual void pushUserType(void* value, const char* name);

	virtual void prepare();
	virtual void exec(int nArgs);
private:
	int _handler;
    lua_State *_state;
};

template <>
inline void LuaHandler::push<int>(int arg)
{ pushInt(arg); }

template <>
inline void LuaHandler::push<unsigned int>(unsigned int arg)
{ pushInt(arg); }

template <>
inline void LuaHandler::push<bool>(bool arg)
{ pushBool(arg); }

template <>
inline void LuaHandler::push<float>(float arg)
{ pushFloat(arg); }

template<>
inline void LuaHandler::push<long long>(long long arg)
{ pushLongLong(arg); }

template<>
inline void LuaHandler::push<const char*>(const char* arg)
{ pushString(arg); }

template<>
inline void LuaHandler::push<std::string>(std::string arg)
{ pushString(arg); }

template<>
inline void LuaHandler::push<const std::string&>(const std::string& arg)
{ pushString(arg); }

#endif //_LUA_HANDLER_H_